package com.nali.dal.mongo.explain.query;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.nali.dal.exception.SyntaxException;
import com.nali.dal.expression.query.Criteria;

/**
 * Condition<br>&nbsp;
 * 收集和整理where参数的类
 * @author sam Created on 2011-3-8
 */
public class Condition {
	private static DBObject NONE_CONDITION=getDBObject(0);
	private static final String $where="$where";
	private Map<String, List<OperateDesc>> conditions;
	private Map<String, OperateDesc> equals;
	private OperateDesc whereExpression;
	private int paramsLength=0;
	
	/**
	 * 添加查询条件
	 */
	public void addNativeWhere(String expression) {
		if(whereExpression==null) {
			whereExpression=new OperateDesc($where, null, expression);
		} else {
			String pre = (String) whereExpression.value;
			if(pre.charAt(0)!='(') {
				pre = '(' + pre + ')';
			}
			whereExpression.value = pre + " && (" + expression + ")"; 
		}
	}

	/**
	 * 添加查询条件
	 */
	public void add(Criteria c, String property) {
		add(property, new OperateDesc(getOperators(c), getParamTraner(c), paramsLength++));
	}
	/**
	 * 添加查询条件
	 */
	public void add(Criteria c, String property, Object value) {
		add(property, new OperateDesc(getOperators(c), getParamTraner(c), value));
	}

	private void add(String property, OperateDesc operateDesc) {
		if(operateDesc.op==null) {
			addToEqualList(property, operateDesc);
		} else {
			if (conditions == null) {
				conditions = new HashMap<String, List<OperateDesc>>();
			}
			List<OperateDesc> list = conditions.get(property);
			if(list==null) {
				list=new LinkedList<OperateDesc>();
				conditions.put(property, list);
			}
			list.add(operateDesc);
		}
	}

	private void addToEqualList(String property, OperateDesc operateDesc) {
		if(equals==null) {
			equals=new HashMap<String, OperateDesc>();
		}
		equals.put(property, operateDesc);
	}
	/**
	 * 验证查询条件的逻辑，目前不能对一个字段同时使用等于和其他操作符
	 */
	public void check() throws SyntaxException {
		if(equals!=null && conditions!=null) {
			for (Entry<String, OperateDesc> entry : equals.entrySet()) {
				String prop = entry.getKey();
				if(conditions.get(prop)!=null) {
					throw new SyntaxException("cannot use equal and other operator together,prop:"+prop);
				}
			}
		}
	}
	public DBObject getQueryDBObject(List<Object> params, int offset) {
		List<Object> realParams = null;
		if(paramsLength>0) {
			realParams=params.subList(offset, paramsLength);
		}
		return getQueryDBObject(realParams);
	}
	/**
	 * 输出结果，供api做cond参数
	 */
	public DBObject getQueryDBObject(List<Object> params) {
		int bsonSize=conditions==null?0:conditions.size();
		bsonSize+=equals==null?0:equals.size();
		bsonSize+=whereExpression==null?0:1;
		if(bsonSize==0) {
			return NONE_CONDITION;
		}
		BasicDBObject bson = getDBObject(bsonSize);
		if(conditions!=null) {
			for (Entry<String, List<OperateDesc>> entry: conditions.entrySet()) {
				String prop = entry.getKey();
				List<OperateDesc> ops = entry.getValue();
				bson.append(prop, getSubBson(ops, params));
			}
		}
		if(equals!=null) {
			for (Entry<String, OperateDesc> entry : equals.entrySet()) {
				String prop = entry.getKey();
				OperateDesc od = entry.getValue();
				bson.append(prop, od.getParam(params));
			}
		}
		if(whereExpression!=null) {
			bson.append(whereExpression.op, whereExpression.getParam(params));
		}
		return bson;
	}
	
	private Object getSubBson(List<OperateDesc> ops, List<Object> params) {
		BasicDBObject bson = getDBObject(ops.size());
		for (int i = 0; i < ops.size(); i++) {
			OperateDesc od = ops.get(i);
			String op = od.op;
			Object p = od.getParam(params);
			bson.append(op, p);
		}
		return bson;
	}

	private static BasicDBObject getDBObject(int size) {
		return new BasicDBObject((int) Math.ceil(size / 0.75f));
	}

	private String getOperators(Criteria c) {
		switch (c) {
		case eq:
		case like:
			return null;//eq特别处理，用null标识
		default:
			return "$"+c;	//TODO 最好改成多个case的形式，暂时可以直接用$拼接枚举类型名称获得
		}
	}
	
	private ValueTraner getParamTraner(Criteria c) {
		switch (c) {
		case like:
			return likeTraner;
		default:
			return null;
		}
	}
	private static final ValueTraner likeTraner=new ValueTraner() {
		@Override
		public Object tran(Object val) {
			if (val instanceof String) {
				String regex = (String) val;
				return Pattern.compile(regex);
			}
			if (val instanceof Pattern) {
				return val;
			}
			throw new IllegalArgumentException("Invalid val type:" + val.getClass() + " val:" + val);
		}
	};

	public int getParamsLength() {
		return paramsLength;
	}
}
class OperateDesc {
	String op;
	private int idx;
	Object value;
	private ValueTraner traner;
	public OperateDesc(String op, ValueTraner traner, int idx) {
		this.op = op;
		this.traner = traner;
		this.idx = idx;
	}
	public OperateDesc(String op, ValueTraner traner, Object value) {
		this.op = op;
		this.traner = traner;
		this.value = value;
		idx = -1;
	}
	public Object getParam(List<Object> params) {
		Object param = getParam0(params);
		if(traner!=null) {
			param=traner.tran(param);
		}
		return param;
	}
	private Object getParam0(List<Object> params) {
		if(idx!=-1) {
			return params.get(idx);
		} else {
			return value;
		}
	}
}
interface ValueTraner {
	Object tran(Object val);
}